`define DELAY(N, clk) begin \
	repeat(N) @(posedge clk);\
	#1ps;\
end

import sync_fifo_pkg::*;

module testbench();

//-------------------------------------{{{common cfg
timeunit 1ns;
timeprecision 1ps;
initial $timeformat(-9,3,"ns",6);

string tc_name;
int tc_seed;

initial begin
    if(!$value$plusargs("tc_name=%s", tc_name)) $error("no tc_name!");
    else $display("tc name = %0s", tc_name);
    if(!$value$plusargs("ntb_random_seed=%0d", tc_seed)) $error("no tc_seed");
    else $display("tc seed = %0d", tc_seed);
end
//-------------------------------------}}}

//-------------------------------------{{{parameter declare
//-------------------------------------}}}

//-------------------------------------{{{signal declare
logic  clk;
logic  rst_n;
logic [WIDTH -1:0] afull_th;
logic [WIDTH -1:0] aempty_th;
logic  winc;
logic [WIDTH -1:0] wdata;
logic  wfull;
logic  almost_wfull;
logic  rinc;
logic [WIDTH -1:0] rdata;
logic  rempty;
logic  almost_rempty;
logic  rvld;
//-------------------------------------}}}

//-------------------------------------{{{clk/rst cfg
initial forever #5ns clk = ~clk;
initial begin
    rst_n = 1'b0;
	`DELAY(30, clk);
	rst_n = 1'b1;
end
initial begin
    #100000ns $finish;
end
//-------------------------------------}}}

//-------------------------------------{{{valid sig assign
//-------------------------------------}}}

//-------------------------------------{{{ready sig assign
//-------------------------------------}}}

//-------------------------------------{{{data  sig assign
logic org_winc;
logic org_rinc;

async_fifo_delay_unit #(
  .WIDTH(1),
  .SYNC_CYC(4))
u_org_winc_delay(
  .clk(clk),
  .rst_n(rst_n),
  .in(org_winc),
  .out(winc)
);

always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        wdata <= 0;
    end else if(winc) begin
        wdata <= wdata + 1'b1;
    end 
end

always @(*) begin
    if(almost_wfull) org_winc = 0;
    else org_winc = 1;
    rinc = org_rinc && !rempty;
end

//-------------------------------------}}}

//-------------------------------------{{{other sig assign
initial begin
    afull_th = 5;
    aempty_th = 1;
    org_rinc = 1'b0;
    `DELAY(60, clk);
    org_rinc = 1;
end

//-------------------------------------}}}

//-------------------------------------{{{rtl inst
sync_fifo #(
    .WIDTH(WIDTH),
    .DEPTH(DEPTH)) 
u_sync_fifo(
    .clk(clk),
    .rst_n(rst_n),
    .afull_th(afull_th),
    .aempty_th(aempty_th),
    .winc(winc),
    .wdata(wdata),
    .wfull(wfull),
    .almost_wfull(almost_wfull),
    .rinc(rinc),
    .rdata(rdata),
    .rempty(rempty),
    .rvld(rvld),
    .almost_rempty(almost_rempty)
);
//-------------------------------------}}}

//-------------------------------------{{{auto_verification
task in_queue_gain();
  while(1)begin
    @(negedge clk);
    if(winc)begin
      data_struct data_in;
      data_in.data = wdata;
      data_in_bus_q.push_back(data_in);
    end
  end//while-end 
endtask: in_queue_gain

task out_queue_gain();
  while(1)begin
    @(negedge clk);
    if(rvld)begin
      data_struct data_out;
      data_out.data = rdata;
      data_out_bus_q.push_back(data_out);
    end
  end//while-end 
endtask: out_queue_gain

task rm_queue_gain();
  while(1)begin
    data_struct data_in;
    data_struct data_out;
    wait(data_in_bus_q.size() > 0);
    data_in = data_in_bus_q.pop_front();
    data_out = data_in;
    rm_q.push_back(data_out);
  end
endtask: rm_queue_gain

task queue_check();
  while(1)begin
    data_struct rm_data;
    data_struct dual_data;
    wait(data_out_bus_q.size() > 0);
    dual_data = data_out_bus_q.pop_front();
    if(rm_q.size() == 0) begin
      $display("dual_data = %0p, rm_queue.size = 0", dual_data);
      error_cnt += 1;
    end
    else begin
      rm_data = rm_q.pop_front();
      if(dual_data != rm_data)begin
        error_cnt += 1;
        $display("dual_data(%0p) != rm_data(%0p) at %t", dual_data, rm_data, $realtime);
      end
      else begin
        //$display("dual_data(%0p) == rm_data(%0p) at %t", dual_data, rm_data, $realtime);
      end
    end
    if(error_cnt >= ERROR_DEBUG_CNT) begin
      $display("Check Error!!!");
      $finish;
    end
  end
endtask: queue_check

initial begin
  fork
    in_queue_gain();
    out_queue_gain();
    rm_queue_gain();
    if(check_en == 1) queue_check();
  join_none
end

//-------------------------------------}}}
endmodule
